<template>
  <div id="page-container">
    <h1>SVG轨迹动画显示工具</h1>
    <input type="file" id="imageUploader" accept="image/*" />
    <button id="trackButton" @click="handleStartDraw">添加标注</button>
    <button id="trackButton" @click="handleShowTrack">显示轨迹</button>
    <div class="svg-container">
      <svg
        id="svgCanvas"
        :style="{ width: width + 'px', height: height + 'px' }"
      ></svg>
      <div
        v-show="contextMenuVisible"
        id="context-menu"
        class="context-menu"
        @click.stop
      >
        <div class="context-menu-item" id="menu-item-1" @click="handleRemove">
          delete
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import * as d3 from "d3";
let points = [
  { time: "2024-06-17T10:00:00", name: "A", x: 50, y: 60, opacity: 1 },
  { time: "2024-06-17T10:05:00", name: "B", x: 150, y: 160, opacity: 1 },
  { time: "2024-06-17T10:10:00", name: "C", x: 250, y: 260, opacity: 1 },
  { time: "2024-06-17T10:15:00", name: "D", x: 350, y: 360, opacity: 1 },
  { time: "2024-06-17T10:15:00", name: "D", x: 360, y: 370, opacity: 1 },
  { time: "2024-06-17T10:15:00", name: "D", x: 380, y: 380, opacity: 1 }
];
export default {
  data() {
    return {
      width: 800,
      height: 600,
      points: [], // 标注的点集合
      animationCircle: [],
      contextMenuVisible: false
    };
  },
  mounted() {
    const imageUploader = document.getElementById("imageUploader");
    this.svg = d3.select("svg");
    let img = (this.img = new Image());
    // 处理图片上传
    imageUploader.addEventListener("change", e => {
      const file = e.target.files[0];
      const reader = new FileReader();
      reader.onload = event => {
        img.onload = () => {
          const scale = this.calcScale(img, this.svg.node());
          this.svg.selectAll("*").remove();
          const drawWidth = scale * Math.max(this.width, img.width);
          const drawHeight = scale * Math.max(this.height, img.height);
          this.svg
            .append("image")
            .attr("xlink:href", event.target.result)
            .attr("width", drawWidth)
            .attr("height", drawHeight)
            .attr("x", (this.width - drawWidth) / 2)
            .attr("y", (this.height - drawHeight) / 2);
        };
        img.src = event.target.result;
      };
      reader.readAsDataURL(file);
    });
    document.addEventListener("click", e => {
      this.contextMenuVisible = false;
    });
  },
  methods: {
    handleStartDraw() {
      // 在SVG上添加点击事件，进行标注
      this.svg.on("click", e => {
        const pointName = prompt("请输入点位名称：");
        const index = this.points.findIndex(item => item.name === pointName)
        if(index > -1) {
          return this.$message.warning('点位名称已存在，请重新输入')
        }
        if (pointName && index === -1) {
          let point = this.getSVGPoint(e);
          console.log("p", point);
          point = { name: pointName, x: point.x, y: point.y };
          this.points.push(point);
          this.drawPoint(point);
        }
      });
    },
    handleRemove() {
      const index = this.points.find(
        item => item.name === this.selectCircle.data.name
      );
      this.selectCircle.circle.remove();
      this.selectCircle.text.remove();
      this.points.splice(index, 1);
      this.contextMenuVisible = false;
    },
    handleShowTrack() {
      // this.svg.innerHTML = ""; // 清空svg
      this.svg.selectAll("*").remove();
      this.startAnimation(points);
    },
    getSVGPoint(event) {
      const rect = this.svg.node().getBoundingClientRect();
      const x = event.clientX - rect.left;
      const y = event.clientY - rect.top;
      const svgPoint = this.svg.node().createSVGPoint();
      svgPoint.x = x;
      svgPoint.y = y;
      return svgPoint;
    },
    drawPoint(point) {
      const { name, x, y } = point;
      // 获取上下文菜单
      const contextMenu = d3.select("#context-menu");
      const circle = this.svg
        .append("circle")
        .attr("cx", x)
        .attr("cy", y)
        .attr("r", 5)
        .attr("class", "point")
        .on("mouseenter", function() {
          d3.select(this).attr("r", 10);
        })
        .on("mouseleave", function() {
          d3.select(this).attr("r", 5);
        })
        .on("contextmenu", (event, d) => {
          event.preventDefault();
          // 获取点击位置
          const [x, y] = d3.pointer(event);
          contextMenu.style("left", `${x}px`).style("top", `${y}px`);
          this.contextMenuVisible = true;
          this.selectCircle = { circle: circle, text: text, data: point };
        });
      const text = this.svg
        .append("text")
        .attr("x", x + 10)
        .attr("y", y - 10)
        .attr("class", "label")
        .text(name);
    },
    calcScale(img, canvas) {
      const w = img.width / canvas.getBoundingClientRect().width;
      const h = img.height / canvas.getBoundingClientRect().height;
      const scale = w > h ? w : h;
      return scale > 1 ? 1 / scale : scale;
    },
    startAnimation(points) {
      // let animationFrameId;
      points.sort((a, b) => new Date(a.time) - new Date(b.time)); // 按
      this.createCirclesSequentially(points, 500);
    },
    createCirclesSequentially(points, delay) {
      points.forEach((point, index) => {
        setTimeout(() => {
          // Append the new circle
          this.svg
            .append("circle")
            .attr("cx", point.x)
            .attr("cy", point.y)
            .attr("r", 5)
            .attr("class", "point")
            .attr("opacity", point.opacity)
            .transition()
            .duration(200)
            .attr("r", 10)
            .transition()
            .duration(500)
            .attr("r", 5);
          this.svg
            .append("text")
            .attr("x", point.x + 10)
            .attr("y", point.y - 10)
            .attr("class", "label")
            .attr("opacity", 1)
            .text(point.name);

          // Reduce the opacity of previously added circles
          this.svg
            .selectAll("circle")
            .filter((d, i) => i < index)
            .attr("opacity", function() {
              return Math.max(0, d3.select(this).attr("opacity") - 0.2);
            });
          this.svg
            .selectAll("text")
            .filter((d, i) => i < index)
            .attr("opacity", function() {
              return Math.max(0, d3.select(this).attr("opacity") - 0.2);
            });
        }, index * delay);
      });
    }
  }
};
</script>

<style>
#svgCanvas {
  border: 1px solid black;
}
.point {
  fill: blue;
  transition: all 0.5s;
}
.label {
  font-size: 16px;
  fill: black;
}
.svg-container {
  position: relative;
}
.context-menu {
  position: absolute;
  background-color: white;
  border: 1px solid #ccc;
  box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5);
  z-index: 1000;
}
.context-menu-item {
  padding: 8px 12px;
  cursor: pointer;
}
.context-menu-item:hover {
  background-color: #f0f0f0;
}
</style>
